/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.catalina.ant;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Redirector;
import org.apache.tools.ant.types.RedirectorElement;

/**
 * Abstract base class to add output redirection support for Catalina Ant tasks.
 * These tasks require Ant 1.5 or later. <br>
 * <strong>WARNING:</strong> due to depends chain, Ant could call a Task more
 * than once and this can affect the output redirection when configured. If you
 * are collecting the output in a property, it will collect the output of only
 * the first run, since Ant properties are immutable and once created they
 * cannot be changed. <br>
 * If you are collecting output in a file the file will be overwritten with the
 * output of the last run, unless you set append="true", in which case each run
 * will append it's output to the file.
 * 
 * 
 * @author Gabriele Garuglieri
 * @version $Revision: 467222 $ $Date: 2006-10-24 11:17:11 +0800 (星期二, 24 十月
 *          2006) $
 * @since 5.5
 */

public abstract class BaseRedirectorHelperTask extends Task {

	// ------------------------------------------------------------- Properties

	/** Redirector helper */
	protected Redirector redirector = new Redirector(this);
	// protected Redirector redirector = null;
	/** Redirector element for this task */
	protected RedirectorElement redirectorElement = null;
	/** The stream for info output */
	protected OutputStream redirectOutStream = null;
	/** The stream for error output */
	protected OutputStream redirectErrStream = null;
	/** The print stream for info output */
	PrintStream redirectOutPrintStream = null;
	/** The print stream for error output */
	PrintStream redirectErrPrintStream = null;

	/**
	 * Whether to fail (with a BuildException) if ManagerServlet returns an
	 * error. The default behavior is to do so. <b> This flag does not control
	 * parameters checking. If the task is called with wrong or invalid
	 * parameters, it will throw BuildException independently from the setting
	 * of this flag.
	 */
	protected boolean failOnError = true;

	/**
	 * <code>true</code> true when output redirection is requested for this
	 * task . Default is to log on Ant log.
	 */
	protected boolean redirectOutput = false;

	/**
	 * will be set to <code>true</code> when the configuration of the
	 * Redirector is complete.
	 */
	protected boolean redirectorConfigured = false;

	/**
	 * Flag which indicates that, if redirected, output should also be always
	 * sent to the log. Default is that otput is sent only to redirected
	 * streams.
	 */
	protected boolean alwaysLog = false;

	/**
	 * Whether to fail (with a BuildException) if ManagerServlet returns an
	 * error. The default behavior is to do so.
	 */
	public void setFailonerror(boolean fail) {
		failOnError = fail;
	}

	/**
	 * Returns the value of the failOnError property.
	 */
	public boolean isFailOnError() {
		return failOnError;
	}

	/**
	 * File the output of the task is redirected to.
	 * 
	 * @param out
	 *            name of the output file
	 */
	public void setOutput(File out) {
		redirector.setOutput(out);
		redirectOutput = true;
	}

	/**
	 * File the error output of the task is redirected to.
	 * 
	 * @param error
	 *            name of the error file
	 * 
	 */
	public void setError(File error) {
		redirector.setError(error);
		redirectOutput = true;
	}

	/**
	 * Controls whether error output is logged. This is only useful when output
	 * is being redirected and error output is desired in the Ant log
	 * 
	 * @param logError
	 *            if true the standard error is sent to the Ant log system and
	 *            not sent to output stream.
	 */
	public void setLogError(boolean logError) {
		redirector.setLogError(logError);
		redirectOutput = true;
	}

	/**
	 * Property name whose value should be set to the output of the task.
	 * 
	 * @param outputProperty
	 *            property name
	 * 
	 */
	public void setOutputproperty(String outputProperty) {
		redirector.setOutputProperty(outputProperty);
		redirectOutput = true;
	}

	/**
	 * Property name whose value should be set to the error of the task..
	 * 
	 * @param errorProperty
	 *            property name
	 * 
	 */
	public void setErrorProperty(String errorProperty) {
		redirector.setErrorProperty(errorProperty);
		redirectOutput = true;
	}

	/**
	 * If true, append output to existing file.
	 * 
	 * @param append
	 *            if true, append output to existing file
	 * 
	 */
	public void setAppend(boolean append) {
		redirector.setAppend(append);
		redirectOutput = true;
	}

	/**
	 * If true, (error and non-error) output will be redirected as specified
	 * while being sent to Ant's logging mechanism as if no redirection had
	 * taken place. Defaults to false. <br>
	 * Actually handled internally, with Ant 1.6.3 it will be handled by the
	 * <code>Redirector</code> itself.
	 * 
	 * @param alwaysLog
	 *            <code>boolean</code>
	 */
	public void setAlwaysLog(boolean alwaysLog) {
		this.alwaysLog = alwaysLog;
		// redirector.setAlwaysLog(alwaysLog);
		redirectOutput = true;
	}

	/**
	 * Whether output and error files should be created even when empty.
	 * Defaults to true.
	 * 
	 * @param createEmptyFiles
	 *            <CODE>boolean</CODE>.
	 */
	public void setCreateEmptyFiles(boolean createEmptyFiles) {
		redirector.setCreateEmptyFiles(createEmptyFiles);
		redirectOutput = true;
	}

	/**
	 * Add a <CODE>RedirectorElement</CODE> to this task.
	 * 
	 * @param redirectorElement
	 *            <CODE>RedirectorElement</CODE>.
	 */
	public void addConfiguredRedirector(RedirectorElement redirectorElement) {
		if (this.redirectorElement != null) {
			throw new BuildException("Cannot have > 1 nested <redirector>s");
		} else {
			this.redirectorElement = redirectorElement;
		}
	}

	/**
	 * Set up properties on the Redirector from RedirectorElement if present.
	 */
	private void configureRedirector() {
		if (redirectorElement != null) {
			redirectorElement.configure(redirector);
			redirectOutput = true;
		}
		/*
		 * Due to depends chain, Ant could call the Task more than once, this is
		 * to prevent that we attempt to configure uselessly more than once the
		 * Redirector.
		 */
		redirectorConfigured = true;
	}

	/**
	 * Set up properties on the Redirector and create output streams.
	 */
	protected void openRedirector() {
		if (!redirectorConfigured) {
			configureRedirector();
		}
		if (redirectOutput) {
			redirector.createStreams();
			redirectOutStream = redirector.getOutputStream();
			redirectOutPrintStream = new PrintStream(redirectOutStream);
			redirectErrStream = redirector.getErrorStream();
			redirectErrPrintStream = new PrintStream(redirectErrStream);
		}
	}

	/**
	 * Ask redirector to close all the streams. It is necessary to call this
	 * method before leaving the Task to have the Streams flush their contents.
	 * If you are collecting output in a property, it will be created only if
	 * this method is called, otherwise you'll find it unset.
	 */
	protected void closeRedirector() {
		try {
			if (redirectOutput) {
				redirector.complete();
			}
		} catch (IOException ioe) {
			log("Error closing redirector: " + ioe.getMessage(),
					Project.MSG_ERR);
		}
		/*
		 * Due to depends chain, Ant could call the Task more than once, this is
		 * to prevent that we attempt to reuse the previuosly closed Streams.
		 */
		redirectOutStream = null;
		redirectOutPrintStream = null;
		redirectErrStream = null;
		redirectErrPrintStream = null;
	}

	/**
	 * Handles output with the INFO priority.
	 * 
	 * @param output
	 *            The output to log. Should not be <code>null</code>.
	 */
	protected void handleOutput(String output) {
		if (redirectOutput) {
			if (redirectOutPrintStream == null) {
				openRedirector();
			}
			redirectOutPrintStream.println(output);
			if (alwaysLog) {
				log(output, Project.MSG_INFO);
			}
		} else {
			log(output, Project.MSG_INFO);
		}
	}

	/**
	 * Handles output with the INFO priority and flushes the stream.
	 * 
	 * @param output
	 *            The output to log. Should not be <code>null</code>.
	 * 
	 */
	protected void handleFlush(String output) {
		handleOutput(output);
		redirectOutPrintStream.flush();
	}

	/**
	 * Handles error output with the ERR priority.
	 * 
	 * @param output
	 *            The error output to log. Should not be <code>null</code>.
	 */
	protected void handleErrorOutput(String output) {
		if (redirectOutput) {
			if (redirectErrPrintStream == null) {
				openRedirector();
			}
			redirectErrPrintStream.println(output);
			if (alwaysLog) {
				log(output, Project.MSG_ERR);
			}
		} else {
			log(output, Project.MSG_ERR);
		}
	}

	/**
	 * Handles error output with the ERR priority and flushes the stream.
	 * 
	 * @param output
	 *            The error output to log. Should not be <code>null</code>.
	 * 
	 */
	protected void handleErrorFlush(String output) {
		handleErrorOutput(output);
		redirectErrPrintStream.flush();
	}

	/**
	 * Handles output with ERR priority to error stream and all other
	 * pritorities to output stream.
	 * 
	 * @param output
	 *            The output to log. Should not be <code>null</code>.
	 */
	protected void handleOutput(String output, int priority) {
		if (priority == Project.MSG_ERR) {
			handleErrorOutput(output);
		} else {
			handleOutput(output);
		}
	}

	/**
	 * Handles output with ERR priority to error stream and all other
	 * pritorities to output stream, then flushes the stream.
	 * 
	 * @param output
	 *            The output to log. Should not be <code>null</code>.
	 */
	protected void handleFlush(String output, int priority) {
		if (priority == Project.MSG_ERR) {
			handleErrorFlush(output);
		} else {
			handleFlush(output);
		}
	}

}
